#include <GTL.h>
#include <GTLEngine.h>
#include "allegro_backend.h"
#include <GTL/external_libs/tinynet/tinynet.h>

#define WIN32_LEAN_AND_MEAN
#undef DrawText
#undef LoadImage
//#include <windows.h>

using namespace GTL;
using namespace GTL::Engine;

class Creature : public GameObject
{
	GTL_DEFINE_PRODUCIBLE_OBJECT(Creature);
public:
	property (Point, Position, Point(400.0f,300.0f), Is::InGroup("Physics"));
	property (Point, Destination, Point(400.0f, 300.0f), Is::InGroup("Physics"));

	state (Alive)
	{
		event (Enter, ())
		{
			Parent->SwitchInto("Standing");
			return 0;
		}

		event (Event, (GameEvent* ev))
		{
			printf("Event!\n");
			return 0;
		}

		state (Standing)
		{
			event (Enter, ())
			{
				Backend->LogInfo("Standing!");
				return 0;
			}
			event (MoveUp, ())
			{
				Parent->Destination = Parent->Position + Point(0.0f, -32.0f);
				Parent->SwitchInto("Moving");
				return 0;
			}
		};

		state (Moving)
		{
			event (Update, (float dt))
			{
				Parent->Position = Parent->Position->GetMovedTowards(Parent->Destination, 5.0f, dt);
				if (Parent->Position == Parent->Destination)
					Parent->SwitchInto("Standing");
				return 0;
			}
		};

		state (Attacking)
		{

		};

		state (Casting)
		{
			
		};
	};
};

class Human : public Creature
{
	GTL_DEFINE_PRODUCIBLE_OBJECT(Human);
public:
	state (WooHoo)
	{
	};
};


class MainMode : public Mode
{
public:
	Font font;
	Image image;
	Image butan;
	GUI::Button client, server, send_pack;
	Human* c;
	tn_listener* listener;
	eastl::vector<tn_connection*> connections_to_server;
	tn_connection* client_conn;

	Shader shader;

	Image target, droplet, window, black;

	struct droplett
	{
		Point pos;
		Point speed;
		droplett* prev, *next;
	};
	//droplett ptz[POINT_COUNT];
	droplett* ptz, *ptz_end;
	Rect AntiGravitatorRects[4];
	//Memory::MemoryPool<droplett> ptz;

	inline void generate(droplett& drop, int i)
	{
		drop.pos = Point(float(rand()%800), -rand()%600);
		drop.speed = Point(0);
	}

	virtual int Load()
	{
		font = Backend->LoadFont("Data/font.ttf");
		butan = Backend->LoadImage("Data/butan.png", true);
		window = Backend->LoadImage("Data/window.jpg", true);
		black = Backend->LoadImage("Data/black.png", true);
		
		client.Set(0, 0, butan, font, "Client").AddToParent(MainGUI);
		server.Set(0, 50, butan, font, "Server").AddToParent(MainGUI);
		send_pack.Set(0, 100, butan, font, "Send").AddToParent(MainGUI);

		client_conn = 0;
		listener = 0;

		c = Produce<Human>();

		c->CallEvent("MoveUp");
		GameEvent ev;
		ev.Caller = 0;
		ev.EventID = 0;
		c->Event(ev);

		tn_init();

		shader = Backend->LoadShader("Data/normal.vert", "Data/normal.frag");

		target = Backend->CreateRenderTarget(800, 600);
		droplet = Backend->LoadImage("Data/droplet5.png", true);

		/*
		for (int i=0; i< POINT_COUNT; i++)
		{
			generate(ptz[i], i);
			ptz[i].pos = Point(float(rand()%800), rand()%600);
		}*/

		d_gen_accum = 0;
		ptz = ptz_end = 0;

		srand(255678);
		for (int i=0; i<4; i++)
		{
			float x = rand()%700, y = rand()%500;
			AntiGravitatorRects[i] = Rect(x, y, x+100, y+100);
		}

		return 0;
	}

	int le;

	virtual int Event(GameEvent event) /// TODO: Finish me
	{
		if (event.EventID == GUI::Event::Click && event.Caller == &server)
		{
			tn_address addr;
			addr.port = 13672;
			listener = tn_create_listener(&addr);
			GTL_ASSERT(!(le = tn_get_last_error()));
		} else if (event.EventID == GUI::Event::Click && event.Caller == &client)
		{
			tn_address addr;
			tn_set_address(&addr, "127.0.0.1", 13672);
			client_conn = tn_connect_to(&addr);
			GTL_ASSERT(!(le = tn_get_last_error()));
		} else if (event.EventID == GUI::Event::Click && event.Caller == &send_pack)
		{
			const char* txt = "Hello bitch!";
			tn_send_data(client_conn, (void*)txt, strlen(txt)+1);
			GTL_ASSERT(!(le = tn_get_last_error()));
		}
		return 0;
	}

	virtual int Unload()
	{
		tn_destroy_listener(listener);
		return 0;
	}

	float d_gen_accum;
	virtual int Update(float dt)
	{
		
		d_gen_accum += dt;
		while ((int)d_gen_accum > 0)
		{
			d_gen_accum -= 0.01f - float(rand()%10)/1000.0f;
			droplett* d = NewDroplet();
			d->pos = Point(float(10-rand()%20), float(10-rand()%20));
			d->speed = Point(Backend->MouseState->X, Backend->MouseState->Y).GetNormalized() * 20.0f;
		}

		if (listener)
		{
			tn_connection* conn = tn_listen(listener);
			GTL_ASSERT(!(le = tn_get_last_error()));
			if (conn)
			{
				printf("Woo, connected!\n");
				connections_to_server.push_back(conn);
			}
			//printf("Listener state: %i\n", listener->state);
		}

		for (size_t i = 0; i<connections_to_server.size(); i++)
		{
			tn_packet* pack = tn_work_connection(connections_to_server[i]);
			GTL_ASSERT(!(le = tn_get_last_error()));
			if (pack)
			{
				printf("Got packet: %s\n", pack->data);
				char res[64];
				sprintf_s(res, "You said: '%s'", pack->data);
				tn_send_data(connections_to_server[i], (char*)res, strlen(res)+1);
				tn_destroy_packet(pack);
			}
		}

		if (client_conn)
		{
			tn_packet* pack = tn_work_connection(client_conn);
			GTL_ASSERT(!(le = tn_get_last_error()));
			//printf("Client connection state: %i\n", client_conn->state);
			if (pack)
			{
				printf("Got packet: %s\n", pack->data);
				tn_destroy_packet(pack);
			}
		}
		c->Update(dt);

		/*
		for (int i=0; i< POINT_COUNT; i++)
		{
			ptz[i].speed += Point(0.0f, 0.1f);
			ptz[i].pos += ptz[i].speed * dt * 20.0f;
			if (ptz[i].pos.y > 600 || ptz[i].pos.x > 800)
			{
				//generate(ptz[i], i);
				generate(ptz[i], i);
			}
		}*/
		for (droplett* d = ptz; d; )
		{
			for (int i=0; i<4; i++)
			{
				if (AntiGravitatorRects[i].Contains(d->pos))
					d->speed += Point(0.0f, -80.0f) * dt;
			}
			d->speed += Point(0.0f, 10.0f) * dt;
			d->pos += d->speed * dt * 20.0f;
			if (d->pos.y > 600 || d->pos.x > 800)
			{
				droplett* t = d;
				d = d->next;
				RemoveDroplet(t);
				continue;
			}
			d = d->next;
		}

		return 0;
	}

	void RemoveDroplet(droplett* d)
	{
		if (d->prev)
			d->prev->next = d->next;
		else
			ptz = d->next;

		if (d->next)
			d->next->prev = d->prev;
		else
			ptz_end = d->prev;

		delete d;
	}

	droplett* NewDroplet()
	{
		droplett* d = new droplett;
		d->next = 0;
		d->prev = ptz_end;
		if (ptz_end)
			ptz_end->next = d;
		ptz_end = d;
		if (!ptz)
			ptz = d;
		return d;
	}

	virtual int Draw(float dt)
	{
		Backend->SetRenderTarget(target.Get());
		Backend->ClearScreen(ColorRGBA(0,0,0,1));
		Backend->SetBlendingMode(BlendMode::Additive);
		//shader->Use();

		/*
		for (int i=0; i< POINT_COUNT; i++)
		{
			droplet->Draw(ptz[i].pos);
		}*/
		for (droplett* d = ptz; d; d = d->next)
		{
			droplet->Draw(d->pos - Point(droplet->Width/2, droplet->Height/2 ));
		}
		
		Backend->SetRenderTarget(0);
		Backend->SetBlendingMode(BlendMode::Alpha);

		window->Draw(Point(0,0));

		/// Rects
		for (int i=0; i<4; i++)
		{
			black->Draw(AntiGravitatorRects[i]);
			//al_draw_filled_rectangle(AntiGravitatorRects[i].p1.x, AntiGravitatorRects[i].p1.y, AntiGravitatorRects[i].p2.x, AntiGravitatorRects[i].p2.y, al_map_rgba_f(0,0,0,1));
		}

		/// Metaballs
		shader->Use();
		float mx = Backend->MouseState->X;
		float my = Backend->MouseState->Y;
		shader->SetUniform(shader->GetUniformID("light_pos"), Point(my/800.0f, my/600.0f));
		target->Draw(Point(0,0));

		//shader->Unuse();
		return 0;
	}
};

class SummonerGame : public GTL::Engine::Game
{
public:
	virtual int StartGame()
	{
		SetNext('main');
		return 0;
	}
	virtual Mode* CreateMode(int id, int arg1, int arg2)
	{
		switch (id)
		{
		case 'main':
			return AutoMembers::Producible::Produce<MainMode>();
		}
		return 0;
	}
private:
	
};

int main()
{
	//Logging::Level = Logging::Trace;
	GTL::Engine::Backend = new AllegroBackend;
	SummonerGame game;
	GTL::Engine::Backend->Init(&game, 800, 600);
	game.Run();

	return 0;
}